(*
  Copyright (c) 2009 Barry Schwartz

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*)

(*-----------------------------------------------------------------------*)

open Box
open CharCode
open Dim
open FontForge
open FontMetric
open MathLayout
open Pycaml
open Python_tools
open Substitute
open SymbolTable
open UniTools
open XNum

(*-----------------------------------------------------------------------*)

let _ =
  register_ocamlpill_types
    [|
      "builder_environment";
      "environment";
      "node";
    |]

let (pythonize_builder_environment, unpythonize_builder_environment) =
  make_pill_wrapping "builder_environment" Builder.void_builder

let (pythonize_environment, unpythonize_environment) =
  make_pill_wrapping "environment" (Environment.empty_environment)

let (pythonize_node, unpythonize_node) =
  make_pill_wrapping "node" (Node.BeginGroup Location.empty_location)

(*-----------------------------------------------------------------------*)

let pythonize_location (name, line, column) =
  pytuple3 (UniText.pythonize name,
            pyint_fromint line,
            pyint_fromint column)

let unpythonize_location_tuple =
  unpythonizing_function
    ~expect_tuple:true
    [| EitherStringType; IntType; IntType |]
    (fun py_args ->
       (UniText.unpythonize py_args.(0),
        pyint_asint py_args.(1),
        pyint_asint py_args.(2)))

let unpythonize_location =
  unpythonizing_function
    ~expect_tuple:true
    [| TupleType |]
    (fun py_args -> unpythonize_location_tuple py_args.(0))

(*-----------------------------------------------------------------------*)

let pythonize_dim d =
  pydict_of_str_association_list
    [("d_base",           pythonize_num d.d_base);
     ("d_stretch_factor", pythonize_num d.d_stretch_factor);
     ("d_stretch_order",  pyint_fromint d.d_stretch_order);
     ("d_shrink_factor",  pythonize_num d.d_shrink_factor);
     ("d_shrink_order",   pyint_fromint d.d_shrink_order)]

let unpythonize_dim py_d =
  { d_base = unpythonize_num (pydict_getitemstring (py_d, "d_base"));
    d_stretch_factor = unpythonize_num (pydict_getitemstring (py_d, "d_stretch_factor"));
    d_stretch_order = pyint_asint (pydict_getitemstring (py_d, "d_stretch_order"));
    d_shrink_factor = unpythonize_num (pydict_getitemstring (py_d, "d_shrink_factor"));
    d_shrink_order = pyint_asint (pydict_getitemstring (py_d, "d_shrink_order")) }

(*-----------------------------------------------------------------------*)

let fm_table = Hashtbl.create 5

let pythonize_font_type t =
  let s =
    match t with
      | OpenTypeCFF -> "OpenTypeCFF"
      | TrueType    -> "TrueType"
      | TFM         -> "TFM"
      | Other       -> "Other"
  in
    pythonize_string s

let pythonize_font_parameter p =
  pydict_of_str_association_list
    [("rule_thickness", pythonize_num p.rule_thickness)]

let pythonize_font_metric fm =
  try
    Hashtbl.find fm_table fm.id
  with
    | Not_found ->
        let glyph_names =
          Array.init
            (fm.last_glyph - fm.first_glyph + 1)
            (fun i -> pybytes_fromstring (fm.get_glyph_name (fm.first_glyph + i)))
        in
        let glyph_name_module = py_module "glyph_name" in
        let unicodes =
          let get_unicode glyph_name =
            py_module_call glyph_name_module "glyph_name_to_unicode" (pytuple_fromsingle glyph_name)
          in
            Array.init
              (fm.last_glyph - fm.first_glyph + 1)
              (fun i -> get_unicode glyph_names.(i))
        in
        let ff_font =
          match fm.ff_font with
            | None -> pynone ()
            | Some font -> FFfont.pythonize font
        in
        let py_fm =
          pydict_of_str_association_list
            [
              ("name",        pythonize_string fm.name);
              ("ps_name",     pythonize_string fm.ps_name);
              ("file_name",   UniText.pythonize fm.file_name);
              ("font_type",   pythonize_font_type fm.font_type);
              ("first_glyph", pyint_fromint fm.first_glyph);
              ("last_glyph",  pyint_fromint fm.last_glyph);
              ("design_size", pythonize_num fm.design_size);
              ("at_size",     pythonize_num fm.at_size);
              ("check_sum",   pythonize_num fm.check_sum);
              ("parameter",   pythonize_font_parameter fm.parameter);
              ("glyph_names", pylist_fromarray glyph_names);
              ("unicodes",    pylist_fromarray unicodes);
              ("ff_font",     ff_font);
              ("code_to_glyph_index", pywrap_int_to_int_function fm.code_to_glyph_index);
            ]
        in
          Hashtbl.add fm_table fm.id py_fm;
          py_fm

let pythonize_stream_contents contents =
  match contents with
    | `PDF_StreamData s -> pytuple2 (pythonize_string "PDF_StreamData",
                                     pythonize_string s)

let rec pythonize_pdf_object obj =
  match obj with
    | `PDF_Null     -> pytuple2 (pythonize_string "PDF_Null",
                                 pynone())
    | `PDF_Name n   -> pytuple2 (pythonize_string "PDF_Name",
                                 UniText.pythonize n)
    | `PDF_String s -> pytuple2 (pythonize_string "PDF_String",
                                 UniText.pythonize s)
    | `PDF_Bool b   -> pytuple2 (pythonize_string "PDF_Bool",
                                 pythonize_bool b)
    | `PDF_Int i    -> pytuple2 (pythonize_string "PDF_Int",
                                 pyint_fromint i)
    | `PDF_Real r   -> pytuple2 (pythonize_string "PDF_Real",
                                 pythonize_num r)
    | `PDF_Array a  -> pytuple2 (pythonize_string "PDF_Array",
                                 pylist_fromarray (Array.of_list (List.map pythonize_pdf_object a)))
    | `PDF_Dict d   -> pytuple2 (pythonize_string "PDF_Dict",
                                 let pythonize_tuple (k, v) =
                                   (UniText.pythonize k, pythonize_pdf_object v)
                                 in
                                 let py_assoc = List.map pythonize_tuple d in
                                   pydict_of_association_list py_assoc)
    | `PDF_Indirect p     -> pytuple2 (pythonize_string "PDF_Indirect",
                                       pythonize_pdf_object p)
    | `PDF_Unencrypted p  -> pytuple2 (pythonize_string "PDF_Unencrypted",
                                       pythonize_pdf_object p)
    | `PDF_Uncompressed p -> pytuple2 (pythonize_string "PDF_Uncompressed",
                                       pythonize_pdf_object p)
    | `PDF_ByteStream (dict, s) -> pytuple2 (pythonize_string "PDF_ByteStream",
                                             pytuple2 (pythonize_pdf_object dict,
                                                       pythonize_string s))
    | `PDF_FileStream (dict, path) -> pytuple2 (pythonize_string "PDF_FileStream",
                                                pytuple2 (pythonize_pdf_object dict,
                                                          UniText.pythonize path))
    | `PDF_ImageStream path -> pytuple2 (pythonize_string "PDF_ImageStream",
                                         UniText.pythonize path)
    | `PDF_Reference obj_no -> pytuple2 (pythonize_string "PDF_Reference",
                                         pyint_fromint obj_no)

let rec unpythonize_pdf_object py_obj =
  let unpythonize_tuple (k, v) =
    (UniText.unpythonize k, unpythonize_pdf_object v)
  in
    try
      match pytype py_obj with

        | NoneType    -> `PDF_Null
        | UnicodeType | BytesType -> `PDF_String (UniText.unpythonize py_obj)
        | BoolType    -> `PDF_Bool (unpythonize_bool py_obj)
        | IntType     -> `PDF_Int (guarded_pyint_asint py_obj)
        | FloatType   -> `PDF_Real (unpythonize_num py_obj)
        | ListType    -> `PDF_Array (List.map unpythonize_pdf_object (Array.to_list (pylist_toarray py_obj)))
        | DictType    -> `PDF_Dict (List.map unpythonize_tuple (pydict_to_association_list py_obj))

        | TupleType ->
            begin
              let a = guarded_pytuple_toarray py_obj in
                if Array.length a = 1 then
                  `PDF_Name (UniText.unpythonize a.(0))
                else
                  let tag = unpythonize_string a.(0) in
                    match tag with
                      | "PDF_Null"   | "null"       -> `PDF_Null
                      | "PDF_Name"   | "name" | "/" -> `PDF_Name (UniText.unpythonize a.(1))
                      | "PDF_String" | "string"     -> `PDF_String (UniText.unpythonize a.(1))
                      | "PDF_Bool"   | "bool"       -> `PDF_Bool (unpythonize_bool a.(1))
                      | "PDF_Int"    | "int"        -> `PDF_Int (guarded_pyint_asint a.(1))
                      | "PDF_Real"   | "real"       -> `PDF_Real (unpythonize_num a.(1))
                      | "PDF_Array"  | "array"      -> `PDF_Array (List.map unpythonize_pdf_object (Array.to_list (pylist_toarray a.(1))))
                      | "PDF_Dict"   | "dict"       -> `PDF_Dict (List.map unpythonize_tuple (pydict_to_association_list a.(1)))

                      | "PDF_Indirect"     | "indirect"     -> `PDF_Indirect (unpythonize_pdf_object a.(1))
                      | "PDF_Unencrypted"  | "unencrypted"  -> `PDF_Unencrypted (unpythonize_pdf_object a.(1))
                      | "PDF_Uncompressed" | "uncompressed" -> `PDF_Uncompressed (unpythonize_pdf_object a.(1))

                      | "PDF_ByteStream" | "bytestream" ->
                          let b = guarded_pytuple_toarray a.(1) in
                            `PDF_ByteStream (unpythonize_pdf_object b.(0), unpythonize_string b.(1))

                      | "PDF_FileStream" | "filestream" ->
                          let b = guarded_pytuple_toarray a.(1) in
                            `PDF_FileStream (unpythonize_pdf_object b.(0), UniText.unpythonize b.(1))

                      | "PDF_ImageStream" | "imagestream" -> `PDF_ImageStream (UniText.unpythonize a.(1))
                      | "PDF_Reference" | "reference" -> `PDF_Reference (guarded_pyint_asint a.(1))

                      | _ -> raise (Pycaml_exn (Pyerr_Exception, "unpythonize_pdf_object error")) (* FIX *)
            end
        | _ -> raise (Pycaml_exn (Pyerr_Exception, "unpythonize_pdf_object error")) (* FIX *)
    with
      | _ -> raise (Pycaml_exn (Pyerr_Exception, "unpythonize_pdf_object error")) (* FIX *)

let pythonize_simple_cmd cmd =
  match cmd with
    | `DVI_Special s -> pytuple2 (pythonize_string "DVI_Special",
                                  pythonize_string s)
    | `PDF_Code s    -> pytuple2 (pythonize_string "PDF_Code",
                                  pybytes_fromstring s)
    | `PDF_Info d    -> pytuple2 (pythonize_string "PDF_Info",
                                  pythonize_pdf_object d)
    | `PDF_Catalog d -> pytuple2 (pythonize_string "PDF_Catalog",
                                  pythonize_pdf_object d)
    | `PDF_Register (d, n) -> pytuple2 (pythonize_string "PDF_Register",
                                        pytuple2 (pythonize_pdf_object d,
                                                  pyint_fromint n))

(*-----------------------------------------------------------------------*)

let pythonize_math_style style =
  let s =
    match style with
      | Display -> "Display"
      | CrampedDisplay -> "CrampedDisplay"
      | Text -> "Text"
      | CrampedText -> "CrampedText"
      | Script -> "Script"
      | CrampedScript -> "CrampedScript"
      | Script2 -> "Script2"
      | CrampedScript2 -> "CrampedScript2"
  in
    pythonize_string s

(*-----------------------------------------------------------------------*)

let pythonize_arg_func pythonizer arg_func =
  python_interfaced_function
    [| CamlpillSubtype "environment" |]
    (fun py_args -> pythonizer (arg_func (unpythonize_environment py_args.(0))))

let pythonize_dim_arg da = pythonize_arg_func pythonize_dim da
let pythonize_skip_arg sa = pythonize_arg_func pythonize_num sa

(*-----------------------------------------------------------------------*)

let pythonize_index_position p =
  let s =
    match p with
      | LeftIndex  -> "LeftIndex"
      | RightIndex -> "RightIndex"
      | VertIndex  -> "VertIndex"
  in
    pythonize_string s

let pythonize_math_code c =
  let s =
    match c with
      | NoMath -> "NoMath"
      | Ordinary -> "Ordinary"
      | BinOp -> "BinOp"
      | Relation -> "Relation"
      | Operator -> "Operator"
      | Punct -> "Punct"
      | Open -> "Open"
      | Close -> "Close"
      | Inner -> "Inner"
      | SubScript -> "SubScript"
      | SuperScript -> "SuperScript"
      | IndexPosition _ -> "IndexPosition"
  in
    match c with
      | IndexPosition p -> pytuple2 (pythonize_string s,
                                     pythonize_index_position p)
      | _ -> pythonize_string s

let pythonize_glyph_text text =
  match text with
    | Substitute.NoGlyphText -> pytuple2 (pythonize_string "NoGlyphText",
                                          pynone ())
    | Substitute.GlyphText s -> pytuple2 (pythonize_string "GlyphText",
                                          pythonize_string (UniText.string_of s))
    | Substitute.ClusterContinuation -> pytuple2 (pythonize_string "ClusterContinuation",
                                                  pynone ())

let pythonize_color col =
  match col with
    | Graphic.Grey n -> pytuple2 (pythonize_string "Gray",
                                  pythonize_num n)
    | Graphic.RGB (r, g, b) -> pytuple4 (pythonize_string "RGB",
                                         pythonize_num r,
                                         pythonize_num g,
                                         pythonize_num b)
    | Graphic.CMYK (c, m, y, k) -> pytuple5 (pythonize_string "CMYK",
                                             pythonize_num c,
                                             pythonize_num m,
                                             pythonize_num y,
                                             pythonize_num k)

let pythonize_num_bezier (a0, a1, a2, a3, a4, a5, a6, a7) =
  pylist_fromarray (Array.of_list (List.map pythonize_num [a0; a1; a2; a3; a4; a5; a6; a7]))

let pythonize_dim_bezier (a0, a1, a2, a3, a4, a5, a6, a7) =
  pylist_fromarray (Array.of_list (List.map pythonize_dim [a0; a1; a2; a3; a4; a5; a6; a7]))

let pythonize_dim_arg_bezier (a0, a1, a2, a3, a4, a5, a6, a7) =
  pylist_fromarray (Array.of_list (List.map pythonize_dim_arg [a0; a1; a2; a3; a4; a5; a6; a7]))

let pythonize_num_path c = pylist_fromarray (Array.of_list (List.map pythonize_num_bezier c))
let pythonize_dim_path c = pylist_fromarray (Array.of_list (List.map pythonize_dim_bezier c))
let pythonize_dim_arg_path c = pylist_fromarray (Array.of_list (List.map pythonize_dim_arg_bezier c))

let pythonize_path_cmd pc =
  match pc with
    | Graphic.Stroke -> pythonize_string "Stroke"
    | Graphic.Fill   -> pythonize_string "Fill"
    | Graphic.Clip   -> pythonize_string "Clip"

let pythonize_line_cap c =
  match c with
    | Graphic.Butt   -> pythonize_string "Butt"
    | Graphic.Circle -> pythonize_string "Circle"
    | Graphic.Square -> pythonize_string "Square"

let pythonize_line_join c =
  match c with
    | Graphic.Miter -> pythonize_string "Miter"
    | Graphic.Round -> pythonize_string "Round"
    | Graphic.Bevel -> pythonize_string "Bevel"

(*-----------------------------------------------------------------------*)

let rec pythonize_gfx_cmd cmd =
  let py tag pythonizer value =
    pytuple2 (pythonize_string tag, pythonizer value)
  in
    match cmd with
      | Graphic.PutBox (dx, dy, b) ->
          pytuple2 (pythonize_string "PutBox",
                    pytuple3 (pythonize_num dx,
                              pythonize_num dy,
                              pythonize_simple_box b))
      | Graphic.SetColour col     -> py "SetColour"     pythonize_color col
      | Graphic.SetBgColour col   -> py "SetBgColour"   pythonize_color col
      | Graphic.SetAlpha a        -> py "SetAlpha"      pythonize_num a
      | Graphic.SetLineWidth w    -> py "SetLineWidth"  pythonize_num w
      | Graphic.SetMiterLimit lim -> py "SetMiterLimit" pythonize_num lim
      | Graphic.SetLineCap c      -> py "SetLineCap"    pythonize_line_cap c
      | Graphic.SetLineJoin c     -> py "SetLineJoin"   pythonize_line_join c
      | Graphic.Draw (pc, c) ->
          pytuple2 (pythonize_string "Draw",
                    pytuple2 (pythonize_path_cmd pc,
                              pythonize_num_path c))

and pythonize_simple_box b =
  match b with
    | Empty -> pytuple_fromsingle (pythonize_string "Empty")
    | SimpleGlyph (index, fm, text) ->
        pytuple2 (pythonize_string "SimpleGlyph",
                  pytuple3 (pyint_fromint index,
                            pythonize_font_metric fm,
                            pythonize_glyph_text text))
    | Rule (width, height) ->
        pytuple2 (pythonize_string "Rule",
                  pytuple2 (pythonize_num width,
                            pythonize_num height))
    | Image (width, height, file_name) ->
        pytuple2 (pythonize_string "Image",
                  pytuple3 (pythonize_num width,
                            pythonize_num height,
                            pythonize_string file_name))
    | Group boxes -> pytuple2 (pythonize_string "Group", pythonize_group [] boxes)
    | Command cmd -> pytuple2 (pythonize_string "Command", pythonize_simple_cmd cmd)

and pythonize_group python_list gfx_cmds =
  match gfx_cmds with
    | [] -> pylist_fromarray (Array.of_list (List.rev python_list))
    | cmd :: more_cmds ->
        let pythonized_cmd = pythonize_gfx_cmd cmd in
          pythonize_group (pythonized_cmd :: python_list) more_cmds

(*-----------------------------------------------------------------------*)

let pythonize_direction d =
  let s =
    match d with
      | `LR      -> "lr"
      | `RL      -> "rl"
      | `Default -> "default"
  in
    pythonize_string s

(*-----------------------------------------------------------------------*)

let py_get_node_tag =
  python_interfaced_function
    [| CamlpillSubtype "node" |]
    begin
      fun py_args ->
        let py_tag =
          match unpythonize_node py_args.(0) with
            | Node.Nodes _n -> "Nodes"
            | Node.Command (_loc, _cmd) -> "Command"
            | Node.CommandBox (_loc, _c) -> "CommandBox"
            | Node.GfxCommand (_loc, _c) -> "GfxCommand"
            | Node.NewGalley (_loc, _n, _m) -> "NewGalley"
            | Node.NewLayout (_loc, _n, _w, _h) -> "NewLayout"
            | Node.NewArea (_loc, _n, _x, _y, _w, _h, _t, _b, _c) -> "NewArea"
            | Node.ShipOut (_loc, _e, _o, _n) -> "ShipOut"
            | Node.AddToGalley (_loc, _g, _n) -> "AddToGalley"
            | Node.PutGalleyInVBox (_loc, _a, _n) -> "PutGalleyInVBox"
            | Node.ModifyGalleyGlue (_loc, _f) -> "ModifyGalleyGlue"
            | Node.Paragraph (_loc, _b) -> "Paragraph"
            | Node.BeginGroup _loc -> "BeginGroup"
            | Node.EndGroup _loc -> "EndGroup"
            | Node.Float (_loc, _n, _b) -> "Float"
            | Node.Glyph (_loc, _i, _n) -> "Glyph"
            | Node.Letter (_loc, _char) -> "Letter"
            | Node.WordEnd (_loc, _nonbreaking) -> "WordEnd"
            | Node.Space _loc -> "Space"
            | Node.Glue (_loc, _w, _h, _i, _d) -> "Glue"
            | Node.Break (_loc, _p, _h, _pre, _post, _no) -> "Break"
            | Node.Rule (_loc, _w, _h, _d) -> "Rule"
            | Node.Image (_loc, _f, _w, _h) -> "Image"
            | Node.Accent (_loc, _a, _c) -> "Accent"
            | Node.HBox (_loc, _d, _b) -> "HBox"
            | Node.HBoxTo (_loc, _d, _w, _b) -> "HBoxTo"
            | Node.HBoxSpread (_loc, _d, _a, _b) -> "HBoxSpread"
            | Node.VBox (_loc, _b) -> "VBox"
            | Node.VBoxTo (_loc, _h, _b) -> "VBoxTo"
            | Node.VBoxSpread (_loc, _a, _b) -> "VBoxSpread"
            | Node.Phantom (_loc, _h, _v, _n) -> "Phantom"
            | Node.HLeaders (_loc, _w, _n) -> "HLeaders"
            | Node.VInsert (_loc, _b, _ns) -> "VInsert"
            | Node.PositionBox (_loc, _f, _n) -> "PositionBox"
            | Node.Table (_loc, _n) -> "Table"
            | Node.TableEntry (_loc, _, _, _, _, _, _) -> "TableEntry"
            | Node.Math (_loc, _n) -> "Math"
            | Node.MathCode (_loc, _c, _n) -> "MathCode"
            | Node.MathChar (_loc, (_cd, _f, _c)) -> "MathChar"
            | Node.SubScript (_loc, _n) -> "SubScript"
            | Node.SuperScript (_loc, _n) -> "SuperScript"
            | Node.Fraction (_loc, _n, _d, _l, _r, _t) -> "Fraction"
            | Node.Underline (_loc, _n) -> "Underline"
            | Node.Overline (_loc, _n) -> "Overline"
            | Node.MathAccent (_loc, _f, _c, _n) -> "MathAccent"
            | Node.Root (_loc, _sf, _sc, _lf, _lc, _n) -> "Root"
            | Node.LeftRight (_loc, _ns) -> "LeftRight"
            | Node.MathStyle (_loc, _s) -> "MathStyle"
            | Node.IndexPosition (_loc, _p) -> "IndexPosition"
        in
          pythonize_string py_tag
    end

let py_get_node_location =
  python_interfaced_function
    [| CamlpillSubtype "node" |]
    begin
      fun py_args ->
        let py_loc =
          match unpythonize_node py_args.(0) with
            | Node.Nodes _n -> Location.empty_location
            | Node.Command (loc, _cmd) -> loc
            | Node.CommandBox (loc, _c) -> loc
            | Node.GfxCommand (loc, _c) -> loc
            | Node.NewGalley (loc, _n, _m) -> loc
            | Node.NewLayout (loc, _n, _w, _h) -> loc
            | Node.NewArea (loc, _n, _x, _y, _w, _h, _t, _b, _c) -> loc
            | Node.ShipOut (loc, _e, _o, _n) -> loc
            | Node.AddToGalley (loc, _g, _n) -> loc
            | Node.PutGalleyInVBox (loc, _a, _n) -> loc
            | Node.ModifyGalleyGlue (loc, _f) -> loc
            | Node.Paragraph (loc, _b) -> loc
            | Node.BeginGroup loc -> loc
            | Node.EndGroup loc -> loc
            | Node.Float (loc, _n, _b) -> loc
            | Node.Glyph (loc, _i, _n) -> loc
            | Node.Letter (loc, _char) -> loc
            | Node.WordEnd (loc, _nonbreaking) -> loc
            | Node.Space loc -> loc
            | Node.Glue (loc, _w, _h, _i, _d) -> loc
            | Node.Break (loc, _p, _h, _pre, _post, _no) -> loc
            | Node.Rule (loc, _w, _h, _d) -> loc
            | Node.Image (loc, _f, _w, _h) -> loc
            | Node.Accent (loc, _a, _c) -> loc
            | Node.HBox (loc, _d, _b) -> loc
            | Node.HBoxTo (loc, _d, _w, _b) -> loc
            | Node.HBoxSpread (loc, _d, _a, _b) -> loc
            | Node.VBox (loc, _b) -> loc
            | Node.VBoxTo (loc, _h, _b) -> loc
            | Node.VBoxSpread (loc, _a, _b) -> loc
            | Node.Phantom (loc, _h, _v, _n) -> loc
            | Node.HLeaders (loc, _w, _n) -> loc
            | Node.VInsert (loc, _b, _ns) -> loc
            | Node.PositionBox (loc, _f, _n) -> loc
            | Node.Table (loc, _n) -> loc
            | Node.TableEntry (loc, _, _, _, _, _, _) -> loc
            | Node.Math (loc, _n) -> loc
            | Node.MathCode (loc, _c, _n) -> loc
            | Node.MathChar (loc, (_cd, _f, _c)) -> loc
            | Node.SubScript (loc, _n) -> loc
            | Node.SuperScript (loc, _n) -> loc
            | Node.Fraction (loc, _n, _d, _l, _r, _t) -> loc
            | Node.Underline (loc, _n) -> loc
            | Node.Overline (loc, _n) -> loc
            | Node.MathAccent (loc, _f, _c, _n) -> loc
            | Node.Root (loc, _sf, _sc, _lf, _lc, _n) -> loc
            | Node.LeftRight (loc, _ns) -> loc
            | Node.MathStyle (loc, _s) -> loc
            | Node.IndexPosition (loc, _p) -> loc
        in
          pythonize_location py_loc
    end

let pythonize_env_cmd cmd =
  python_interfaced_function
    [| TupleType; CamlpillSubtype "environment" |]
    begin
      fun py_args ->
        let loc = unpythonize_location py_args.(0) in
        let env = unpythonize_environment py_args.(1) in
          pythonize_environment (cmd loc env)
    end

let pythonize_node_list n =
  pythonize_list pythonize_node n

(*-----------------------------------------------------------------------*)

let pythonize_page p =
  pydict_of_str_association_list
    [ ("p_contents", pythonize_simple_box p.p_contents);
      ("p_number",   pyint_fromint p.p_number);
      ("p_width",    pythonize_num p.p_width);
      ("p_height",   pythonize_num p.p_height) ]

let py_evaluate_ast =
  python_interfaced_function
    [| ListType |]
    (fun py_args ->
       let nodes = List.map unpythonize_node (Array.to_list (pylist_toarray py_args.(0))) in
       let pages = Evaluate.evaluate nodes in
         pylist_fromarray (Array.of_list (List.map pythonize_page pages)))

let py_eval_node_list =
  python_interfaced_function
    [| CamlpillSubtype "environment";
       CamlpillSubtype "builder_environment";
       ListType |]
    (fun py_args ->
       let env = unpythonize_environment py_args.(0) in
       let builder = unpythonize_builder_environment py_args.(1) in
       let nodes = List.map unpythonize_node (Array.to_list (pylist_toarray py_args.(2))) in
         pythonize_environment (Evaluate.eval_node_list env builder nodes))

let py_eval_node =
  python_interfaced_function
    [| CamlpillSubtype "environment";
       CamlpillSubtype "builder_environment";
       CamlpillSubtype "node" |]
    (fun py_args ->
       let env = unpythonize_environment py_args.(0) in
       let builder = unpythonize_builder_environment py_args.(1) in
       let node = unpythonize_node py_args.(2) in
         pythonize_environment (Evaluate.eval_node env builder node))

let py_get_pages =
  python_interfaced_function
    [| CamlpillSubtype "environment" |]
    (fun py_args ->
       let env = unpythonize_environment py_args.(0) in
       let pages = Environment.get_pages env in
         pylist_fromarray (Array.of_list (List.map pythonize_page pages)))

let py_void_builder =
  python_interfaced_function
    [| |]
    (fun _py_args -> pythonize_builder_environment Builder.void_builder)

let py_initialise_environment =
  python_interfaced_function
    [| |]
    (fun _py_args -> pythonize_environment (Environment.initialise_environment ()))

(*-----------------------------------------------------------------------*)

let _ =
  register_for_python
    [|
      ("evaluate_ast", py_evaluate_ast);
      ("eval_node_list", py_eval_node_list);
      ("eval_node", py_eval_node);
      ("get_pages", py_get_pages);
      ("void_builder", py_void_builder);
      ("initialise_environment", py_initialise_environment);
      ("initialize_environment", py_initialise_environment);
      ("get_node_location", py_get_node_location);
      ("get_node_tag", py_get_node_tag);
    |]

(*-----------------------------------------------------------------------*)
